package com.foreverwin.mes.so.service.impl;

import com.foreverwin.mes.common.constants.HandleBOConstants;
import com.foreverwin.mes.core.exception.BusinessException;
import com.foreverwin.mes.core.utils.StringUtils;
import com.foreverwin.mes.meapi.model.Bom;
import com.foreverwin.mes.meapi.model.BomComponent;
import com.foreverwin.mes.meapi.model.Item;
import com.foreverwin.mes.meapi.service.BomService;
import com.foreverwin.mes.meapi.service.ItemService;
import com.foreverwin.mes.so.dto.BomComponentLink;
import com.foreverwin.mes.so.dto.InactiveTimeDto;
import com.foreverwin.mes.so.dto.ShopOrderExpandReq;
import com.foreverwin.mes.so.dto.ShopOrderExpandResult;
import com.foreverwin.mes.so.model.CycleTimeConfig;
import com.foreverwin.mes.so.model.ShopOrderGenHis;
import com.foreverwin.mes.so.service.CycleTimeConfigService;
import com.foreverwin.mes.so.service.ShopOrderExpandService;
import com.foreverwin.mes.so.service.ShopOrderGenHisService;
import com.sap.me.demand.*;
import com.sap.me.extension.Services;
import com.visiprise.globalization.DateGlobalizationServiceInterface;
import com.visiprise.globalization.GlobalizationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

/**
 * <p>
 * 需求展工单服务实现
 * </p>
 *
 * @author Syngna
 * @since 2020-03-03
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class ShopOrderExpandServiceImpl implements ShopOrderExpandService {

    private final Logger logger = LoggerFactory.getLogger(ShopOrderExpandServiceImpl.class);

    @Autowired
    private ItemService itemService;

    @Autowired
    private BomService bomService;

    @Autowired
    private CycleTimeConfigService cycleTimeConfigService;

    @Autowired
    private ShopOrderGenHisService shopOrderGenHisService;

    /**
     * 展工单
     *
     * @param shopOrderExpandReq
     * @return
     */
    @Override
    public ShopOrderExpandResult doExpand(ShopOrderExpandReq shopOrderExpandReq) {
        String site = shopOrderExpandReq.getSite();
        ShopOrderExpandResult shopOrderExpandResult = new ShopOrderExpandResult();
        String plannedItem = shopOrderExpandReq.getPlannedItem();
        String destinationItem = shopOrderExpandReq.getDestinationItem();
        Item plannedItemPo = itemService.selectById(StringUtils.genHandle(HandleBOConstants.ITEM_BO, site, plannedItem, "#"));
        Item destinationItemPo = null;
        if (plannedItemPo == null) {
            throw BusinessException.build("物料【" + plannedItem + "】当前版本不存在");
        }
        if (StringUtils.notBlank(destinationItem) && !plannedItem.equals(destinationItem)) {
            destinationItemPo = itemService.selectById(StringUtils.genHandle(HandleBOConstants.ITEM_BO, site, destinationItem, "#"));
            if (destinationItemPo == null) {
                throw BusinessException.build("目标产品【" + destinationItem + "】当前版本不存在");
            }
        }
        Item rootItemPo = plannedItemPo;
        BigDecimal rootDemandQty = new BigDecimal(shopOrderExpandReq.getDemandQty());
        Date rootCompDate = shopOrderExpandReq.getPlannedItemCompDate();
        if (StringUtils.notBlank(destinationItem) && !plannedItem.equals(destinationItem)) {
            // 推到计划物料需求量对应的目标产品数量及完成时间，并将根节点置为目标产品
            // 从顶部到计划物料获取所有组件，过滤掉反推无用的组件
            BomComponentLink bomComponentLink = new BomComponentLink();
            bomComponentLink.setComponentBo(destinationItemPo.getHandle());
            Map<String, BomComponentLink> finalComponentMap = new HashMap();
            getComponentLink(site, destinationItemPo.getBomBo(), plannedItem, bomComponentLink, finalComponentMap, 0);

            BomComponentLink plannedItemComponent = finalComponentMap.get("component");
            if (StringUtils.notBlank(destinationItem) && !plannedItem.equals(destinationItem) && plannedItemComponent == null) {
                throw BusinessException.build("计划物料【" + plannedItem + "】不是目标产品【" + destinationItem + "】的组件");
            }
            // 由计划物料开始向目标物料循环
            while (plannedItemComponent.getParent() != null) {
                BigDecimal qty = plannedItemComponent.getComponentQty();
                // 计算上一级物料需求量
                rootDemandQty = rootDemandQty.divide(qty, 3, BigDecimal.ROUND_HALF_UP);
                // 计算上一级物料完成时间
                // parent的开始时间为：当前物料的完成时间，parent的完成时间为：parent的开始时间+parent物料对应的平均ct时间 * parent需求数量 + parent物料对应的平均呆滞时间
                List<CycleTimeConfig> cycleTimeConfigList = cycleTimeConfigService.selectByItemAndOperation(site, plannedItemComponent.getParent().getComponentBo(), plannedItemComponent.getOperation());
                BigDecimal totalCycleTime = BigDecimal.ZERO;
                BigDecimal averageCycleTime = BigDecimal.ZERO;
                if (cycleTimeConfigList != null && cycleTimeConfigList.size() > 0) {
                    for (CycleTimeConfig cycleTimeConfig : cycleTimeConfigList) {
                        totalCycleTime = totalCycleTime.add(new BigDecimal(cycleTimeConfig.getCycleTime()));
                    }
                    averageCycleTime = totalCycleTime.divide(new BigDecimal(cycleTimeConfigList.size()), 6, BigDecimal.ROUND_HALF_UP);
                    rootCompDate = new Date(rootCompDate.getTime() + averageCycleTime.multiply(rootDemandQty).longValue() * 1000);
                }
                // 加上平均呆滞时间
                List<InactiveTimeDto> inactiveTimeList = cycleTimeConfigService.selectInactiveTime(site, StringUtils.trimHandle(plannedItemComponent.getParent().getComponentBo()));
                BigDecimal totalInactiveTime = BigDecimal.ZERO;
                BigDecimal averageInactiveTime;
                int totalCount = 0;
                if (inactiveTimeList != null && inactiveTimeList.size() > 0) {
                    for (InactiveTimeDto inactiveTime : inactiveTimeList) {
                        if(StringUtils.notBlank(inactiveTime.getLowerLimit())) {
                            totalCount ++;
                            totalInactiveTime = totalInactiveTime.add(new BigDecimal(inactiveTime.getLowerLimit()));
                        }
                    }
                    averageInactiveTime = totalCount > 0 ? totalInactiveTime.divide(new BigDecimal(totalCount), 6, BigDecimal.ROUND_HALF_UP) : BigDecimal.ZERO;
                    rootCompDate = new Date(rootCompDate.getTime() + averageInactiveTime.multiply(new BigDecimal("3600000")).longValue());
                }
                plannedItemComponent = plannedItemComponent.getParent();
            }

            rootItemPo = destinationItemPo;
        }

        doExpand(site, shopOrderExpandReq, rootItemPo, rootDemandQty.stripTrailingZeros().toPlainString(), rootCompDate, shopOrderExpandResult, 0);


        return shopOrderExpandResult;
    }

    /**
     * 工单开立
     *
     * @param shopOrderExpandResultList
     * @return
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Override
    public List<ShopOrderExpandResult> generateShopOrder(String site, String user, List<ShopOrderExpandResult> shopOrderExpandResultList) {
        DateGlobalizationServiceInterface dateGlobalizationService = (DateGlobalizationServiceInterface) GlobalizationService.getUserService("com.visiprise.globalization.DateGlobalizationService");
        ShopOrderServiceInterface shopOrderServiceInterface = Services.getService("com.sap.me.demand", "ShopOrderService", site);
        String transBatch = StringUtils.createQUID();
        for (ShopOrderExpandResult shopOrderExpandResult : shopOrderExpandResultList) {
            try {
                CreateShopOrderRequest createShopOrderRequest = new CreateShopOrderRequest();
                // createShopOrderRequest.setShopOrder("");
                createShopOrderRequest.setPlannedItemRef(shopOrderExpandResult.getItemBo());
                createShopOrderRequest.setStatus(ShopOrderStatus.Releasable);
                createShopOrderRequest.setShopOrderType(ShopOrderType.REPETITIVE);
                createShopOrderRequest.setQuantityToBuild(new BigDecimal(shopOrderExpandResult.getQty()));
                createShopOrderRequest.setPriority(new BigDecimal("500"));
                if (shopOrderExpandResult.getStartDate() != null) {
                    createShopOrderRequest.setPlannedStartDate(dateGlobalizationService.createDateTime(shopOrderExpandResult.getStartDate().getTime()));
                }
                if (shopOrderExpandResult.getCompDate() != null) {
                    createShopOrderRequest.setPlannedCompletionDate(dateGlobalizationService.createDateTime(shopOrderExpandResult.getCompDate().getTime()));
                }
                ShopOrderFullConfiguration shopOrderFullConfiguration = shopOrderServiceInterface.createShopOrder(createShopOrderRequest);
                shopOrderExpandResult.setShopOrder(shopOrderFullConfiguration.getShopOrder());
                // 记录历史
                ShopOrderGenHis shopOrderGenHis = new ShopOrderGenHis();
                shopOrderGenHis.setSite(site);
                shopOrderGenHis.setCreatedDateTime(new Date());
                shopOrderGenHis.setCreateUser(user);
                shopOrderGenHis.setItemBo(shopOrderExpandResult.getItemBo());
                shopOrderGenHis.setQty(Double.parseDouble(shopOrderExpandResult.getQty()));
                shopOrderGenHis.setShopOrder(shopOrderFullConfiguration.getShopOrder());
                shopOrderGenHis.setTransactionBatch(transBatch);
                shopOrderGenHisService.insert(shopOrderGenHis);
                logger.info("shop order expand auto created [{}]", shopOrderFullConfiguration.getShopOrder());
            } catch (Exception e) {
                String msg = StringUtils.isBlank(e.getMessage()) ? e.toString() : e.getMessage();
                shopOrderExpandResult.setErrorMsg(msg.length() > 1024 ? msg.substring(0, 1024) : msg);
            }
        }
        return shopOrderExpandResultList;
    }

    private BomComponentLink getComponentLink(String site, String bomBo, String plannedItem, BomComponentLink bomComponentLink, Map<String, BomComponentLink> finalComponentMap, int count) {

        if (StringUtils.notBlank(bomBo)) {
            Bom bom = bomService.getBomInfo(site, bomBo);
            if (bom != null && bom.getBomComponentList() != null && bom.getBomComponentList().size() > 0) {
                for (BomComponent bomComponent : bom.getBomComponentList()) {
                    BomComponentLink child = new BomComponentLink();
                    child.setComponentBo(bomComponent.getComponentGbo());
                    child.setComponentQty(new BigDecimal(bomComponent.getQty()));
                    child.setOperation(StringUtils.trimHandle(bomComponent.getOperationBo()));
                    child.setParent(bomComponentLink);
                    if (StringUtils.trimHandle(bomComponent.getComponentGbo()).equals(plannedItem)) {
                        child.setMatch(true);
                        finalComponentMap.put("component", child);
                        break;
                    }
                    Item childItem = itemService.selectById(bomComponent.getComponentGbo());
                    if (count > 1000) {
                        throw BusinessException.build("请检查bom组件是否循环引用");
                    }
                    getComponentLink(site, childItem.getBomBo(), plannedItem, child, finalComponentMap, ++count);
                }
            }
        }
        return bomComponentLink;
    }

    private void doExpand(String site, ShopOrderExpandReq shopOrderExpandReq, Item item, String demandQty, Date compDate, ShopOrderExpandResult shopOrderExpandResult, int count) {
        String bomBo = item.getBomBo();
        shopOrderExpandResult.setItemBo(item.getHandle());
        shopOrderExpandResult.setItem(item.getItem());
        shopOrderExpandResult.setItemDescription(item.getDescription());
        shopOrderExpandResult.setQty(demandQty);
        shopOrderExpandResult.setUnitOfMeasure(item.getUnitOfMeasure());
        shopOrderExpandResult.setCompDate(compDate);
        // 消除反向推导导致的计划物料数量与时间误差
        if(item.getItem().equals(shopOrderExpandReq.getPlannedItem())) {
            shopOrderExpandResult.setCompDate(shopOrderExpandReq.getPlannedItemCompDate());
            shopOrderExpandResult.setQty(shopOrderExpandReq.getDemandQty());
        }
        if (StringUtils.notBlank(bomBo)) {
            Bom bom = bomService.getBomInfo(site, bomBo);
            if (bom != null && bom.getBomComponentList() != null && bom.getBomComponentList().size() > 0) {
                // 根据CT时间和呆滞时间获取工单开始时间
                String operation = null;
                for (BomComponent bomComponent : bom.getBomComponentList()) {
                    if (StringUtils.notBlank(bomComponent.getOperationBo())) {
                        operation = StringUtils.trimHandle(bom.getBomComponentList().get(0).getOperationBo());
                        break;
                    }
                }
                if (StringUtils.notBlank(operation)) {
                    // 减去CT时间
                    shopOrderExpandResult.setStartDate(compDate);
                    List<CycleTimeConfig> cycleTimeConfigList = cycleTimeConfigService.selectByItemAndOperation(site, item.getHandle(), operation);
                    BigDecimal totalCycleTime = BigDecimal.ZERO;
                    BigDecimal averageCycleTime = BigDecimal.ZERO;
                    if (cycleTimeConfigList != null && cycleTimeConfigList.size() > 0) {
                        for (CycleTimeConfig cycleTimeConfig : cycleTimeConfigList) {
                            totalCycleTime = totalCycleTime.add(new BigDecimal(cycleTimeConfig.getCycleTime()));
                        }
                        averageCycleTime = totalCycleTime.divide(new BigDecimal(cycleTimeConfigList.size()), 6, BigDecimal.ROUND_HALF_UP);
                        shopOrderExpandResult.setStartDate(new Date(compDate.getTime() - averageCycleTime.multiply(new BigDecimal(demandQty)).longValue() * 1000));
                    }
                }
                // 减去平均呆滞时间
                List<InactiveTimeDto> inactiveTimeList = cycleTimeConfigService.selectInactiveTime(site, item.getItem());
                BigDecimal totalInactiveTime = BigDecimal.ZERO;
                BigDecimal averageInactiveTime;
                int totalCount = 0;
                if (inactiveTimeList != null && inactiveTimeList.size() > 0) {
                    for (InactiveTimeDto inactiveTime : inactiveTimeList) {
                        if(StringUtils.notBlank(inactiveTime.getLowerLimit())) {
                            totalCount ++;
                            totalInactiveTime = totalInactiveTime.add(new BigDecimal(inactiveTime.getLowerLimit()));
                        }
                    }
                    averageInactiveTime = totalCount > 0 ? totalInactiveTime.divide(new BigDecimal(totalCount), 6, BigDecimal.ROUND_HALF_UP) : BigDecimal.ZERO;
                    shopOrderExpandResult.setStartDate(new Date(shopOrderExpandResult.getStartDate().getTime() - averageInactiveTime.multiply(new BigDecimal("3600000")).longValue()));
                }
                for (BomComponent bomComponent : bom.getBomComponentList()) {
                    if (!"1".equals(bomComponent.getMaterialType()) && !"2".equals(bomComponent.getMaterialType())) {
                        continue;
                    }
                    if (item.getHandle().equals(bomComponent.getComponentGbo())) {
                        throw BusinessException.build("物料【" + item.getItem() + "】存在循环引用组件");
                    }
                    Item childItem = itemService.selectById(bomComponent.getComponentGbo());
                    if (childItem == null) {
                        throw BusinessException.build("组件物料【" + StringUtils.trimHandle(bomComponent.getComponentGbo()) + "】不存在");
                    }

                    ShopOrderExpandResult child = new ShopOrderExpandResult();
                    String childQty = new BigDecimal(demandQty).multiply(new BigDecimal(bomComponent.getQty())).setScale(3, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString(); // TODO
                    Date childCompDate = shopOrderExpandResult.getStartDate(); // 子组件完成时间应该在父组件的开始时间之前，取临界值

                    child.setComponentQty(new BigDecimal(bomComponent.getQty()));
                    if (count > 1000) {
                        throw BusinessException.build("请检查bom组件是否循环引用");
                    }
                    doExpand(site, shopOrderExpandReq, childItem, childQty, childCompDate, child, ++count);
                    if (shopOrderExpandResult.getComponents() == null) {
                        shopOrderExpandResult.setComponents(new ArrayList<ShopOrderExpandResult>());
                    }
                    shopOrderExpandResult.getComponents().add(child);
                }
            }
        }
    }
}